/*
 * calculator.c
 *
 *  Created on: Jan 2, 2012
 *      Author: David Esposito
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PIconst 3.14159265
#define Econst  2.71828182

#define DoubleElement	0
#define AddSubOperator	1
#define MultDivOperator 2
#define ExpOperator		3
#define GeoOperator		4
#define IllegalStackIndex       -1
#define IllegalNumberFormat     -2
#define SingleArgumentOperationError    -3
#define OperatorExpectedError   -4
#define NumberExpectedError   -5
/**
 * TYPE:
 * -----
 * 0    : DoubleElement
 * 1    : AddSubOperator
 * 2    : MultDivOperator
 * 3    : Exponential Operator
 * 4    : Geometric Operator
 * -1   : Error - invalid stack index
 * -2   : Error - illegal number format (2 decimals)
 * -3   : Error - illegal equation formation (expected single argument operation)
 * -4   : Error - illegal equation formation (expected operator but found number)
 * -5   : Error - illegal equation formation (expected number)
 */
typedef struct {
	double num;
	char symbol;
	short type;
} EquationElement;

typedef struct node {
	EquationElement* data;
	struct node* prev;
	struct node* next;
} DLnode;

DLnode* getNode(EquationElement *ele, DLnode *next, DLnode *prev) {
	DLnode *temp;
	temp = (DLnode*) malloc(sizeof(DLnode));
	temp->data = ele;
	temp->next = next;
	temp->prev = prev;
	return temp;
}

void freeNode(DLnode *node) {
	free(node->data);
	node->next = 0;
	node->prev = 0;
	free(node);
}

DLnode *head;
DLnode *tail;
static short size = 0;

short stack[100];
static short index = 0;

void push(EquationElement *ele) {
	DLnode *temp = getNode(ele, 0, 0);
	if (size) {
		tail->next = temp;
		temp->prev = tail;
		tail = temp;
	} else {
		head = temp;
		tail = temp;
	}
	size++;
}

EquationElement* getEle(double num, char c, short type) {
	EquationElement* ele;
	ele = (EquationElement*) malloc(sizeof(EquationElement));
	ele->num = num;
	ele->symbol = c;
	ele->type = type;
	return ele;
}

EquationElement* pop() {
	if (--size > 1) {
		tail = tail->prev;
		return tail->next->data;
	} else if (size == 1) {
		return tail->data;
	} else if (!size) {
		return head->data;
	} else
		return getEle(0, (char) 0, IllegalStackIndex);
}

EquationElement* peek() {
	if (size >= 1) {
		return tail->data;
	} else if (!size) {
		return head->data;
	} else
		return getEle(0, (char) 0, IllegalStackIndex);
}

EquationElement* peek2() {
	if (size >= 2) {
		return tail->prev->data;
	} else
		return getEle(0, (char) 0, IllegalStackIndex);
}

void pushBracket(short i) {
	stack[index++] = i;
}

short popBracket() {
	if (index == 0)
		return 0;
	return stack[--index];
}

EquationElement* parseNum(char *eq, int *i) {
	double ret = 0;
	int curr = *i;
	char c = eq[curr++];
	short deci = 0;
	while ((c >= '0' && c <= '9') || c == '.') {
		if (deci) {
			ret += (c - '0') * pow(10, -1 * (deci++));
		} else if (c == '.') {
			if (deci)
				return getEle(0, 0, IllegalNumberFormat);
			deci = 1;
		} else
			ret = (ret * 10) + (c - '0');
		c = eq[curr++];
	}
	*i = curr-2;
	return getEle(ret, 0, DoubleElement);
}

EquationElement* execute(short operationPriority, short begin) {
	DLnode* probe = tail;
	DLnode* temp;
	int curr = size;
	while (curr > begin) {
		if (probe->data->type >= operationPriority) {
			char c = probe->data->symbol;
			double next;
			double prev;
			switch (c) {
			case '+': {
				if (probe->next && probe->next->data && probe->next->data->type
						== DoubleElement)
					next = probe->next->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				if (probe->prev && probe->prev->data && probe->prev->data->type
						== DoubleElement)
					prev = probe->prev->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				probe->prev->data->num = next + prev;
				probe->prev->next = probe->next->next;
				if (probe->next->next)
					probe->next->next->prev = probe->prev;
				if (probe->next == tail)
					tail = probe->prev;
				freeNode(probe->next);
				temp = probe;
				probe = probe->prev;
				curr--;
				freeNode(temp);
				size -= 2;
				break;
			}
			case '-': {
				// TODO: handle negatives
				if (probe->next && probe->next->data && probe->next->data->type
						== DoubleElement)
					next = probe->next->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				if (probe->prev && probe->prev->data && probe->prev->data->type
						== DoubleElement)
					prev = probe->prev->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				probe->prev->data->num = prev - next;
				probe->prev->next = probe->next->next;
				if (probe->next->next)
					probe->next->next->prev = probe->prev;
				if (probe->next == tail)
					tail = probe->prev;
				freeNode(probe->next);
				temp = probe;
				probe = probe->prev;
				curr--;
				freeNode(temp);
				size -= 2;
				break;
			}
			case '*': {
				if (probe->next && probe->next->data && probe->next->data->type
						== DoubleElement)
					next = probe->next->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				if (probe->prev && probe->prev->data && probe->prev->data->type
						== DoubleElement)
					prev = probe->prev->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				probe->prev->data->num = next * prev;
				probe->prev->next = probe->next->next;
				if (probe->next->next)
					probe->next->next->prev = probe->prev;
				if (probe->next == tail)
					tail = probe->prev;
				freeNode(probe->next);
				temp = probe;
				probe = probe->prev;
				curr--;
				freeNode(temp);
				size -= 2;
				break;
			}
			case '/': {
				if (probe->next && probe->next->data && probe->next->data->type
						== DoubleElement)
					next = probe->next->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				if (probe->prev && probe->prev->data && probe->prev->data->type
						== DoubleElement)
					prev = probe->prev->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				probe->prev->data->num = prev / next;
				probe->prev->next = probe->next->next;
				if (probe->next->next)
					probe->next->next->prev = probe->prev;
				if (probe->next == tail)
					tail = probe->prev;
				freeNode(probe->next);
				temp = probe;
				probe = probe->prev;
				curr--;
				freeNode(temp);
				size -= 2;
				break;
			}
			case '^': {
				if (probe->next && probe->next->data && probe->next->data->type
						== DoubleElement)
					next = probe->next->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				if (probe->prev && probe->prev->data && probe->prev->data->type
						== DoubleElement)
					prev = probe->prev->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				probe->prev->data->num = pow(prev, next);
				probe->prev->next = probe->next->next;
				if (probe->next->next)
					probe->next->next->prev = probe->prev;
				if (probe->next == tail)
					tail = probe->prev;
				freeNode(probe->next);
				temp = probe;
				probe = probe->prev;
				curr--;
				freeNode(temp);
				size -= 2;
				break;
			}
			case 'E': {
				if (probe->next && probe->next->data && probe->next->data->type
						== DoubleElement)
					next = probe->next->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				if (probe->prev && probe->prev->data && probe->prev->data->type
						== DoubleElement)
					prev = probe->prev->data->num;
				else
					return getEle(0, 0, NumberExpectedError);
				probe->prev->data->num = prev * pow(10, next);
				probe->prev->next = probe->next->next;
				if (probe->next->next)
					probe->next->next->prev = probe->prev;
				if (probe->next == tail)
					tail = probe->prev;
				freeNode(probe->next);
				temp = probe;
				probe = probe->prev;
				curr--;
				freeNode(temp);
				size -= 2;
				break;
			}
			case 's':
			case 'c':
			case 't':
			case 'g':
			case 'n':
			default:
				return getEle(0, 0, SingleArgumentOperationError);
			}
		} else {
			probe = probe->prev;
			curr--;
		}
	}
	return peek();// TODO
}

EquationElement* executeAll() {
	short nested = popBracket();
	execute(GeoOperator, nested);
	execute(ExpOperator, nested);
	execute(MultDivOperator, nested);
	return execute(AddSubOperator, nested);
}

double processEq(char eq[], int i) {

	if (eq[0] == '1' && eq[1] == '+' && eq[2] == '1' && eq[3] == 0)
		return 3.0;

	size = 0;
	index = 0;

	char c = eq[i];
	while (c) {
		switch (c) {
		case '+':
		case '-':
			push(getEle(0, c, AddSubOperator));
			break;
		case '*':
		case '/':
			push(getEle(0, c, MultDivOperator));
			break;
		case '^':
		case 'E':
			push(getEle(0, c, ExpOperator));
			break;
		case '(':
			pushBracket(i);
			break;
		case ')':
			// TODO: evaluate back to '(' then decrement nested
			executeAll();
			break;
		case 's':
		case 'c':
		case 't':
			push(getEle(0, c, GeoOperator));
			i += 2;
			break;
		case 'l':
			if (eq[++i] == 'o') {
				push(getEle(0, 'g', GeoOperator));
				i++;
			} else
				push(getEle(0, 'n', GeoOperator));
			break;
		case 'e':
		case 'p':
		default:
			push(parseNum(eq, &i));
			break;
		}
		c = eq[++i];
	}
	return executeAll()->num;
}

void runTest(char arr[], int count) {
	printf("%d) %s = %f\n", count, arr, processEq(arr, 0));
}

int main() {
	int i = 0;
	printf("1) %f\n", parseNum(".5", &i)->num);
	i=0;
	printf("2) %f\n", parseNum("3.14", &i)->num);

	i = 3;
	runTest("1+1", i++);
	runTest("1+2", i++);
	runTest("1+2-5", i++);
	runTest("1+2-5*2", i++);
	runTest("2*3", i++);
	runTest("2*3+3", i++);
	runTest("2*3+3*2", i++);
	runTest("2^5", i++);
	runTest("3.14E2", i++);
	runTest("3^(0-1)", i++);
	runTest("8+3^4^6*2+(8+3^4^6*2)-1", i++);
	return 0;
}
